package com.gitee.wsl.android.ui.recyclerView.adapter


import android.util.SparseArray
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.paging.*
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.RecyclerView
import com.gitee.wsl.android.ui.recyclerView.adapter.AdapterConfig.Companion.FLAG_MULTI_SELECT_ABLE
import com.gitee.wsl.android.ui.recyclerView.adapter.AdapterConfig.Companion.FLAG_SELECT_ABLE
import com.gitee.wsl.android.ui.recyclerView.adapter.loadstate.PagingLoadStateAdapter
import com.gitee.wsl.android.ui.recyclerView.adapter.viewholder.VIEW_HOLDER_TYPE_EMPTY
import com.gitee.wsl.android.ui.recyclerView.adapter.viewholder.EmptyViewHolder
import com.gitee.wsl.android.ui.recyclerView.adapter.viewholder.Subscriber
import com.gitee.wsl.android.ui.recyclerView.animation.AdapterAnimationHelper
import com.gitee.wsl.android.ui.recyclerView.callback.OnViewAttachStateChangeListener
import com.gitee.wsl.android.ui.recyclerView.callback.ViewAttachStateChangeListenerContainer
import com.gitee.wsl.ext.base.DEFAULT_BASE_FLAG
import com.gitee.wsl.ext.base.isTrue
import com.gitee.wsl.ext.base.trueValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.lang.ref.WeakReference

open class PagingBuilderAdapter<T:Any,AR: com.gitee.wsl.android.ui.recyclerView.adapter.ActionRepository<T>>(
                                       protected val adapterConfig: AdapterConfig<T,AR>
) : PagingDataAdapter<T,RecyclerView.ViewHolder>(adapterConfig.diffCallback),LayoutChangeSet,PagingDateHelperScope<T>,
    com.gitee.wsl.android.ui.recyclerView.adapter.SelectHelperScope<T>,
    ViewAttachStateChangeListenerContainer {

    protected val defaultViewHolderFactoryCache=adapterConfig.viewHolderFactoryCache

    private val sparseArrayLayoutInflater = SparseArray<WeakReference<LayoutInflater>>(1)

    var layoutType:Int= AdapterConfig.LAYOUT_TYPE_DEFAULT

    var selectHelper: com.gitee.wsl.android.ui.recyclerView.adapter.SampleSelectHelper<T>? = adapterConfig.hasFlag(FLAG_SELECT_ABLE or FLAG_MULTI_SELECT_ABLE).trueValue {
        com.gitee.wsl.android.ui.recyclerView.adapter.SampleSelectHelper(
            this,
            adapterConfig.hasFlag(FLAG_MULTI_SELECT_ABLE)
        )
    }

    var pagingDataHelper:PagingDateHelper<T>? = adapterConfig.hasFlag(FLAG_PAGING_DATA_EDIT_ABLE).trueValue { PagingDateHelper(this) }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if(viewType== VIEW_HOLDER_TYPE_EMPTY) return EmptyViewHolder(parent)
        return defaultViewHolderFactoryCache[viewType].getViewHolder(
            parent, sparseArrayLayoutInflater.get(0).get() ?: LayoutInflater.from(parent.context)
        )
    }

    init{
        if(getRepository()!=null && adapterConfig.repository==null)
            adapterConfig.repository=getRepository()
        if(selectHelper!=null && adapterConfig.selectHelper==null)
            adapterConfig.selectHelper=selectHelper
        if(pagingDataHelper!=null && adapterConfig.pagingDataHelper==null)
            adapterConfig.pagingDataHelper=pagingDataHelper

        selectHelper?.let {
            this.registerAdapterDataObserver(it)
        }

        adapterConfig.itemFlagManger?.apply {
            init { getItem( it) }
            registerAdapterDataObserver(observer)
        }
    }

    override fun onBindViewHolder(
        holder: RecyclerView.ViewHolder,
        position: Int,
        payloads: List<Any>
    ) {
        if (position != RecyclerView.NO_POSITION) {
            getItem(position)?.apply {
                if (holder is Subscriber<*,*>) {
                    holder as Subscriber<T,AR>
                    holder.onBindViewHolder(position, payloads,getItem(position),adapterConfig)
                }
            }
        }
    }

    fun getRepository(): AR?=null

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        onBindViewHolder(holder,position, emptyList())
    }

    override fun getItemViewType(position: Int): Int {
        var pre:T?=null
        var next:T?=null
        (position>0).isTrue { pre=getItem(position-1) }
        (position<itemCount).isTrue { next=getItem(position+1) }
        return adapterConfig.itemTypeCallBack?.let { it(position,getItem(position),pre,next,layoutType) }?: VIEW_HOLDER_TYPE_EMPTY
    }

    suspend fun submit(pagingData: PagingData<T>) {
        submitData(tranPagingData(pagingData))
    }

    fun submit(lifecycle: Lifecycle, pagingData: PagingData<T>) {
        val tranData = tranPagingData(pagingData)
        pagingDataHelper?.setPagingData( lifecycle.coroutineScope , tranData)
        submitData(lifecycle, tranData)
    }

    private fun tranPagingData(pagingData: PagingData<T>):PagingData<T>{
        val filter=adapterConfig.filterHandler
        return  if(filter==null)
            pagingData
        else
            pagingData.filter { filter( it)  }
    }

    fun withLoadStateHeader(): ConcatAdapter {
        return withLoadStateHeader(PagingLoadStateAdapter())
    }

    fun withLoadStateFooter(): ConcatAdapter {
        return withLoadStateFooter(PagingLoadStateAdapter())
    }

    var animatorAdapter: AdapterAnimationHelper?=null

    fun ableAnimation(animatorAdapter: AdapterAnimationHelper = AdapterAnimationHelper()){
        addOnViewAttachStateChangeListener(animatorAdapter)
    }

    fun disableAnimation(){
        animatorAdapter?.let {
            removeOnViewAttachStateChangeListener(it)
        }
        animatorAdapter = null
    }

    override fun layoutChange(layoutType: Int) {
        this.layoutType=layoutType
    }


    override fun getData(position: Int): T? {
        if (position < 0 || position >= itemCount) return null
        return try {
            getItem(position)
        } catch (e: Exception) {
            Timber.d(e)
            null
        }
    }

    companion object{
        const val FLAG_PAGING_DATA_EDIT_ABLE= DEFAULT_BASE_FLAG shl 5
    }

    private val mOnViewAttachStateChangeListeners: MutableList<OnViewAttachStateChangeListener> by lazy {
        mutableListOf()
    }

    override fun addOnViewAttachStateChangeListener(listener: OnViewAttachStateChangeListener) {
        mOnViewAttachStateChangeListeners.add(listener)
    }

    override fun removeOnViewAttachStateChangeListener(listener: OnViewAttachStateChangeListener) {
        mOnViewAttachStateChangeListeners.remove(listener)
    }

    /**
     * Called when a view created by this holder has been attached to a window.
     * simple to solve item will layout using all
     * [asStaggeredGridFullSpan]
     *
     * @param holder
     */
    @CallSuper
    override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
        super.onViewAttachedToWindow(holder)
        mOnViewAttachStateChangeListeners.forEach {
            it.onViewAttachedToWindow(holder)
        }
    }

    @CallSuper
    override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
        mOnViewAttachStateChangeListeners.forEach {
            it.onViewDetachedFromWindow(holder)
        }
    }

}

interface PagingDateHelperScope<T : Any>{

    fun getItemCount():Int

   suspend fun submitData(pagingData: PagingData<T>)

   fun getData(position: Int): T?

   fun notifyItemChanged(position: Int, payload: Any?)
}

interface PagingDateHelperAction<T : Any>:
    com.gitee.wsl.android.ui.recyclerView.adapter.ActionRepository<T> {
    /**
     * 采用setPagingData 可以动态增减数据
     */
    fun setPagingData(scope: CoroutineScope, pagingData: PagingData<T>)

    /**
     * 向首部添加数据
     */
    fun prependItem(item: T)


    /**
     * 向尾部添加数据
     */
    fun appendItem(item: T)

    /**
     * 过滤数据
     * @param predicate 条件为false的移除，为true的保留
     */
    fun filterItem(predicate: suspend (T) -> Boolean)

    /**
     * 移除数据
     * @param item 要移除的条目
     */
    fun removeItem(item: T)


    /**
     * 移除数据
     * @param position 要移除的条目的索引
     */
    fun removeItem(position: Int)

    /**
     * 修改条目内容
     * @param position 条目索引
     * @param payload 局部刷新
     */
    fun edit(@androidx.annotation.IntRange(from=0) position: Int, payload: Any? = null, block: (T?) -> Unit = {})
}


open class PagingDateHelper<T : Any>(val callback:PagingDateHelperScope<T>/*, private val selectHelper:SelectHelperAction<T>?=null*/):PagingDateHelperAction<T>{

    /**
     * 保存提交的数据集
     */
    protected lateinit var mPagingData: PagingData<T>

    /**
     * 协程
     */
    protected lateinit var mScope: CoroutineScope

    /**
     * 采用setPagingData 可以动态增减数据
     */
    override fun setPagingData(scope: CoroutineScope, pagingData: PagingData<T>) {
        mScope = scope
        mPagingData = pagingData
        submitPagingData()
    }

    //<editor-fold desc="数据处理">

    /**
     * 提交数据
     */
    private fun submitPagingData() {
        mScope.launch {
            callback.submitData(mPagingData)
        }
    }

    /**
     * 向尾部添加数据
     */
    override fun appendItem(item: T) {
        if (!this::mPagingData.isInitialized || !this::mScope.isInitialized) {
            throw IllegalArgumentException("To add data, you must use the 'setPagingData' method")
        }
        mPagingData = mPagingData.insertFooterItem(item = item)
        submitPagingData()
    }

    /**
     * 向首部添加数据
     */
    override fun prependItem(item: T) {
        if (!this::mPagingData.isInitialized || !this::mScope.isInitialized) {
            throw IllegalArgumentException("To add data, you must use the 'setPagingData' method")
        }
        mPagingData = mPagingData.insertHeaderItem(item = item)
        submitPagingData()
    }

    /**
     * 过滤数据
     * @param predicate 条件为false的移除，为true的保留
     */
    override fun filterItem(predicate: suspend (T) -> Boolean) {
        if (!this::mPagingData.isInitialized || !this::mScope.isInitialized) {
            throw IllegalArgumentException("To edit data, you must use the 'setPagingData' method")
        }
        mPagingData = mPagingData.filter(predicate)
        submitPagingData()
    }

    /**
     * 移除数据
     * @param item 要移除的条目
     */
    override fun removeItem(item: T) {
        filterItem { it != item }
    }

    /**
     * 移除数据
     * @param position 要移除的条目的索引
     */
    override fun removeItem(position: Int) {
        filterItem { it != callback.getData(position) }
    }

    /**
     * 修改条目内容
     * @param position 条目索引
     * @param payload 局部刷新
     */
    override fun edit(@androidx.annotation.IntRange(from=0) position: Int, payload: Any? , block: (T?) -> Unit ) {
        if (position >= callback.getItemCount()) {
            return
        }
        block(callback.getData(position))
        callback.notifyItemChanged(position, payload)
    }
}

